#ifndef TRI_FirstPersonCamera_H_
#define TRI_FirstPersonCamera_H_

#include "scene.h"
#include "base/vector.h"
#include "base/matrix.h"

namespace tri{

class TFirstPersonCamera: public sigslot::has_slots<sigslot::multi_threaded_local>{
public:
	TFirstPersonCamera(tri::TScene* scene){
		camRotX =0;
		camRotY =0;
		mStepSize = 1;
		mUpAngleLimit = 80;
		mDownAngleLimit = -80;
		DEFAULT_CAM_FRONT = tri::TVector(0,0,1);
		mScene = scene;
		mScene->connect<TFirstPersonCamera>(tri::KEY_PRESSED,this,&TFirstPersonCamera::key_pressed);
		mScene->connect<TFirstPersonCamera>(tri::MOUSE_MOVE,this,&TFirstPersonCamera::onMouseMove);
		reset();
	}
	
	virtual ~TFirstPersonCamera(){}
	
	void rotateX(float degrees){
	}
	
	void rotateY(float degrees){
	}
	
	void setUpAngleLimit(float deg){
		mUpAngleLimit = deg;
	}
	
	void setDownAngleLimit(float deg){
		mDownAngleLimit =deg;
	}
	
	void moveForward(float units){
	    tri::TVector pos;
		tri::TVector step;
		
		pos = mScene->getCamera()->getPosition();
		step = CAM_FRONT;
		step.normalize();
		pos+=step*units;
		pos.y = 0.5;
		mScene->getCamera()->setPosition(pos);
		updateCameraRotation();
	}
	
	void setStepSize(float units){
		mStepSize = units;
	}
	
	void reset(){
		camRotX =0;
		camRotY =0;
		updateCameraRotation();
	}
	
private:

	void updateCameraRotation(){
		tri::TMatrix mRotMatrix;
		getRotationMatrixXYZ(camRotX,camRotY ,0,&mRotMatrix);
		tri::multVMat(&mRotMatrix, &DEFAULT_CAM_FRONT, &CAM_FRONT);
		
		tri::TVector pos = mScene->getCamera()->getPosition();
		pos += CAM_FRONT;
		mScene->getCamera()->setLookAtVector(pos);
	}
	
	void key_pressed(int key){
		
		switch(key){
		case 'w':
			moveForward(mStepSize);
			break;
		case 's':
			moveForward(-mStepSize);
			break;
		}
	}
	
	void onMouseMove(){
		tri::TMouseEvent event = tri::getMouseEvent();
		float x_move = event.getRelativeX();
		float y_move = event.getRelativeY();
		
		camRotX -=y_move;
		camRotX = camRotX > mUpAngleLimit ? mUpAngleLimit : camRotX;
		camRotX = camRotX < mDownAngleLimit ? mDownAngleLimit : camRotX;
		camRotY +=x_move;
		updateCameraRotation();
	}
	
	

	tri::TScene* mScene;
	float camRotX;
	float camRotY;
	float mStepSize;
	float mUpAngleLimit;
	float mDownAngleLimit;
	tri::TVector CAM_FRONT;
	tri::TVector DEFAULT_CAM_FRONT;
};

}

#endif
